﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Timers;
using System.Windows.Media.Animation;


namespace SGGWElevator
{
    /// <summary>
    /// Interaction logic for ElevatorIcon.xaml
    /// Author: Magdalena Adamowicz
    /// </summary>
    public partial class ElevatorCabinControl : UserControl
    {
        ElevatorStatus _actualElevatorStatus;
        /// <summary>
        /// Contains acutal elevator status. 
        /// </summary>
        public ElevatorStatus ActualElevatorStatus
        {
            get { return _actualElevatorStatus; }
            set { _actualElevatorStatus = value; }
        }


        int _floorHeight = 0;
        /// <summary>
        /// Height of a single floor.
        /// </summary>
        public int FloorHeight
        {
            get { return _floorHeight; }
        }


        /// <summary>
        /// Contains start points for every floor,
        /// for ex.: for first floor it's (0,0), for second floor it's (0, _floorHeight + 1) and so on.
        /// </summary>
        public BiDictionary<Floor, int> FloorsStartingPoints
        {
            get { return _floorsStartingPoints; }
            protected set { _floorsStartingPoints = value; }
        }
        protected BiDictionary<Floor, int> _floorsStartingPoints;

        // Constructor
        public ElevatorCabinControl()
        {
            InitializeComponent();

            // Create animation instances
            CreateAnimations();

            // Calculate floor heights and starting points
            FloorsStartingPoints = CalculateFloorsMeasurements(this.Height, ExtFloor.Floors.Count(), out _floorHeight);

            // Set position
            SetPosition(_floorsStartingPoints.GetByFirst(Floor.Lobby));

        }

        /// <summary>
        /// Approxim medium point between cabin's top and bottom.
        /// </summary>
        /// <returns></returns>
        public double GetPosition()
        {
            double height = this.ActualHeight != 0 ? this.ActualHeight : this.Height;
            return height - (Cabin.Margin.Top + Configuration.CABIN_HEIGHT / 2) ;
        }

        /// <summary>
        /// Sets elevator cabin position.
        /// </summary>
        /// <param name="position">Value of top margin.</param>
        public void SetPosition(double position)
        {
            double height = this.ActualHeight != 0 ? this.ActualHeight : this.Height;
            double marginTop = height - position;

            Cabin.Dispatcher.Invoke(new Action(delegate()
            {
                Cabin.Margin = new Thickness(0, marginTop, 0, 0);
            }));
        }

        /// <summary>
        /// Opens elevators door.
        /// </summary>
        public void OpenDoor()
        {
            // Start elevator animation
            FadeOut(LockBackground);
        }


        /// <summary>
        /// Closes elevators door.
        /// </summary>
        public void CloseDoor()
        {
            // Start animation
            FadeIn(LockBackground);
        }


        /// <summary>
        /// Sets every floor starting point and their height.
        /// </summary>
        BiDictionary<Floor, int> CalculateFloorsMeasurements(double totalHeight, int floorsNumber, out int floorHeight)
        {
            // Check given arguments
            if (totalHeight <= 0 | floorsNumber <= 0)
                throw new ArgumentException();

            // Calculate floor height
            floorHeight = (int)(totalHeight / floorsNumber);

            // Create starting point for every floor
            var floorsList = ExtFloor.Floors.ToArray();
            var dict = new BiDictionary<Floor, int>();

            for (int i = 0; i < floorsNumber; i++)
                dict.Add(floorsList[i], (floorHeight + 1) * (i + 1));

            return dict;
        }


        #region Animation
        /// <summary>
        /// Fade in animation.
        /// </summary>
        private DoubleAnimation _fadeIn;

        /// <summary>
        /// Fade out animation.
        /// </summary>
        private DoubleAnimation _fadeOut;

        /// <summary>
        /// Creates animation instances.
        /// </summary>
        public void CreateAnimations()
        {
            // Prepare fadeIn animation
            {
                if (_fadeIn == null)
                    _fadeIn = new DoubleAnimation();

                _fadeIn.Duration = new Duration(
                    TimeSpan.FromSeconds(AnimationProperties.Duration));
                _fadeIn.From = 0.0;
                _fadeIn.To = 1.0;
                _fadeIn.AutoReverse = false;
                _fadeIn.RepeatBehavior = new RepeatBehavior(1);
            }


            // Prepare fadeOut animation
            {
                if (_fadeOut == null)
                    _fadeOut = new DoubleAnimation();

                _fadeOut.Duration = new Duration(
                    TimeSpan.FromSeconds(AnimationProperties.Duration));
                _fadeOut.From = 1.0;
                _fadeOut.To = 0.0;
                _fadeOut.AutoReverse = false;
                _fadeOut.RepeatBehavior = new RepeatBehavior(1);
            }
        }


        /// <summary>
        /// Smoothly makes selected object fades in. 
        /// </summary>
        /// <param name="obj">Selected image.</param>
        /// <returns>Has the operation ended successfully.</returns>
        void FadeIn(IAnimatable obj)
        {
            Cabin.Dispatcher.Invoke(new Action(delegate()
            {
                obj.BeginAnimation(UIElement.OpacityProperty, _fadeIn);
            }));

        }


        /// <summary>
        /// Smoothly makes selected object fades out.
        /// </summary>
        /// <param name="obj"></param>
        void FadeOut(IAnimatable obj)
        {
            Cabin.Dispatcher.Invoke(new Action(delegate()
            {
                obj.BeginAnimation(UIElement.OpacityProperty, _fadeOut);
            }));
        }

        #endregion


        /// <summary>
        /// Paths to images.
        /// </summary>
        class ImagePaths
        {
            public readonly static string stripes = @"/Images/ElevatorControl/Stripes.png";
            public readonly static string elevatorIcon = @"/Images/ElevatorControl/ElevatorIcon.png";
        }


        /// <summary>
        /// Set of animation properties.
        /// </summary>
        public class AnimationProperties
        {
            /// <summary>
            /// Default animation duration.
            /// </summary>
            const double defaultDuration = 1;


            private static double _duration = 0;
            /// <summary>
            /// Animation duration.
            /// </summary>
            public static double Duration
            {
                get
                {
                    if (_duration <= 0)
                        return defaultDuration;
                    else
                        return _duration;
                }
                set
                {
                    if (value <= 0)
                        throw new Exception("negative duration value");

                    _duration = value;
                }
            }
        }
    }
}
